#pragma once
#include<iostream>
#include<string>
#include<vector>
#include<unordered_map>
#include<fstream>
#include<mutex>
#include"Util.hpp"
#include"Logger.hpp"

namespace Index
{
struct word_cnt
{
    int title_cnt;
    int content_cnt;
};


struct DocInfo
{
    uint64_t doc_id; //文档id
    std::string _title; //文档标题
    std::string _content; //文档内容
    std::string _url; //文档的url
};

struct InvertedElement
{
    uint64_t doc_id; //文档id
    std::string _key_word; //文档关键字
    int weight; //文档权重
};

class index
{
private:
    //对于正排索引来说,数组下标天然可以当作文档id,便可以通过id来找到文档所对应的 标题/内容/url 等信息
    std::vector<DocInfo> _forward_index; 
    //对于倒排索引来说,一个关键字key_word 可能对应多个文档
    std::unordered_map<std::string,std::vector<InvertedElement>> _invert_index;
    std::unordered_map<std::string,word_cnt> _word_hz;
private:
    index()
    {}

    index(const index&) = delete;
    index& operator=(const index&) = delete;
    static index* _instance;
    static std::mutex _mutex;

public:
    DocInfo* GetForwardIndex(uint64_t id) //获取一个正排索引(即根据doc_id找到文档所对应的内容信息)
    {
        if(id >= _forward_index.size())
        {
            std::cout << "id is out_of_range" << std::endl;
            return nullptr;
        }
        for(auto& e : _forward_index)
        {
            if(e.doc_id == id)
            {
                return &e;
            }
        }
        
        return nullptr;
    }

    static index* GetInstance()
    {
        if(_instance == nullptr)
        {
            _mutex.lock();
            if(_instance == nullptr)
            {
                _instance = new index();
            }
            _mutex.unlock();
        }

        return _instance;
    }

    std::vector<InvertedElement>* GetInvertIndex(const std::string& _key_word) //获取一个倒排拉链
    {
        auto iter = _invert_index.find(_key_word);
        if(iter == _invert_index.end())
        {
            std::cout << _key_word << " have no invert_index !" << std::endl;
            return nullptr;
        }

        return &(iter->second);
    }

    bool BuildIndex(const std::string& input) //根据文件内容构建正倒排索引
    {
        std::ifstream in(input,std::ios::in | std::ios::binary);
        if(!in.is_open())
        {
            std::cout << input << "open error!" << std::endl;
            return false;
        }

        std::string _line;
        int count = 0;
        while(std::getline(in,_line))
        {
            DocInfo* ret = BulidForwardIndex(_line);
            if(ret == nullptr)
            {
                std::cout << "bulid the forward index error!" << std::endl;
                continue;
            }

            BulidInvertIndex(*ret);
            count++;
            if(count % 50 == 0)
                LOG(NORMAL,"当前已经建立的索引文档: " + std::to_string(count));
            
        }
        return true;
    }


    ~index()
    {}


private:
    DocInfo* BulidForwardIndex(const std::string& _line)
    {
        const std::string sep = "\3";
        std::vector<std::string> _results;
        Util::StringUtil::Split(_line,&_results,sep);
        if(_results.size() != 3)
        {
            return nullptr;
        }
        DocInfo _doc;
        _doc._title = _results[0];
        _doc._content = _results[1];
        _doc._url = _results[2];
        _doc.doc_id = _forward_index.size();
        _forward_index.push_back(std::move(_doc));
        std::cout << "构建正排索引成功:" << _doc.doc_id << std::endl;

        return &_forward_index.back();

    }

    
#define X 10
#define Y 2

    bool BulidInvertIndex(const DocInfo& doc)
    {
        std::vector<std::string> _title_word;
        Util::JiebaUtil::CutString(doc._title,&_title_word);

        for(auto word : _title_word)
        {
            boost::to_lower(word);
            _word_hz[word].title_cnt++;
        }
        std::vector<std::string> _content_word;
        Util::JiebaUtil::CutString(doc._content,&_content_word);

        for(auto word : _content_word)
        {
            boost::to_lower(word);
            _word_hz[word].content_cnt++;
        }

        InvertedElement element;
        for(auto& word : _word_hz)
        {
            element.doc_id = doc.doc_id;
            element._key_word = word.first;
            element.weight = X*word.second.title_cnt + Y*word.second.content_cnt;

            _invert_index[word.first].push_back(std::move(element));

        }

        return true;

    }
    


};

index* index::_instance = nullptr;
std::mutex index::_mutex;
}